/*****************************************************************************************************************************
  This project was supported by the National Basic Research 973 Program of China
under Grant No.2011CB302301 Huazhong University of Science and Technology (HUST)
Wuhan National Laboratory for Optoelectronics

FileName: ssd.c
Author: Hu Yang		Version: 2.1	Date:2011/12/02
Description:

History:
<contributor>     <time>        <version>       <desc> <e-mail> Yang Hu
2009/09/25	      1.0		    Creat SSDsim yanghu@foxmail.com
2010/05/01        2.x           Change
Zhiming Zhu     2011/07/01        2.0           Change 812839842@qq.com Shuangwu
Zhang  2011/11/01        2.1           Change               820876427@qq.com
Chao Ren        2011/07/01        2.0           Change 529517386@qq.com Hao Luo
2011/01/01        2.0           Change               luohao135680@gmail.com
 *****************************************************************************************************************************/

#include "ssd.h"

int get_write_amount(struct ssd_info *ssd) {
  char file_name[50];
  int write_amount;
  FILE *data_file = fopen("write_data1.txt", "r");
  do {
    fscanf(data_file, "%s %d", file_name, &write_amount);
    printf("%s  %s\n", file_name, ssd->tracefilename);
  } while (strcmp(ssd->tracefilename, file_name) != 0);
  return write_amount;
}

/********************************************************************************************************************************
  1，main函数中initiatio()函数用来初始化ssd,；2，make_aged()函数使SSD成为aged，aged的ssd相当于使用过一段时间的ssd，里面有失效页，
  non_aged的ssd是新的ssd，无失效页，失效页的比例可以在初始化参数中设置；3，pre_process_page()函数提前扫一遍读请求，把读请求
  的lpn<--->ppn映射关系事先建立好，写请求的lpn<--->ppn映射关系在写的时候再建立，预处理trace防止读请求是读不到数据；4，simulate()是
  核心处理函数，trace文件从读进来到处理完成都由这个函数来完成；5，statistic_output()函数将ssd结构中的信息输出到输出文件，输出的是
  统计数据和平均数据，输出文件较小，trace_output文件则很大很详细；6，free_all_node()函数释放整个main函数中申请的节点
 *********************************************************************************************************************************/
int main(int argc, char const *argv[]) {
  unsigned int i, j, k;
  struct ssd_info *ssd;

#ifdef DEBUG
  printf("enter main\n");
#endif

  ssd = (struct ssd_info *)malloc(sizeof(struct ssd_info));
  alloc_assert(ssd, "ssd");
  memset(ssd, 0, sizeof(struct ssd_info));
  // argv[0] ssd执行文件 argv[1] trace路径  argv[2] 输出目录
  if (argc == 3) {
    strncpy(ssd->parameterfilename, "page.parameters", 16);
    strcpy(ssd->outputfilename, argv[2]);
    strcpy(ssd->statisticfilename, argv[2]);
    strcpy(ssd->tracefilename, argv[1]);
    printf("filename:%s\n", ssd->tracefilename);
    char delims[] = "/";
    char *path = NULL;
    char *tracename = NULL;
    char p[30];
    strcpy(p, argv[1]);
    path = strtok(p, delims);
    tracename = strtok(NULL, delims);
    if (tracename == NULL) {  //如果trace文件即在当前文件夹
      tracename = path;
    }
    printf("path:%s tracename:%s\n", path, tracename);

    strcat(ssd->outputfilename, tracename);
    strcat(ssd->outputfilename, "_out");
    printf("outputfilename:%s\n", ssd->outputfilename);
    strcat(ssd->statisticfilename, tracename);
    strcat(ssd->statisticfilename, "_st");
    printf("statisticfilename:%s\n", ssd->statisticfilename);

    strcpy(ssd->sumfilename, argv[2]);
    strcat(ssd->sumfilename, "stat.txt");
    printf("sumfilename:%s\n", ssd->sumfilename);
  } else {
    printf("error");
    return -1;
  }
  ssd = initiation(ssd);
  // printf("gc hard threshold=%lf\n",ssd->parameter->page_block *
  // ssd->parameter->block_plane * ssd->parameter->gc_hard_threshold);
  srand(2021);  //随机种子
  make_aged(ssd);
  pre_process_page(ssd);
  for (i = 0; i < ssd->parameter->channel_number; i++) {
    for (j = 0; j < ssd->parameter->die_chip; j++) {
      for (k = 0; k < ssd->parameter->plane_die; k++) {
        printf("%d,0,%d,%d:  %5d\n", i, j, k, ssd->channel_head[i].chip_head[0].die_head[j].plane_head[k].free_page);
      }
    }
  }
  if (ssd->parameter->gc_hard_threshold != 0) {
    int amount = get_write_amount(ssd);
    printf("write amount:%d\n", amount);
    int avg_write_amount = amount / 512 / 8;

    float new_gc_hard = (ssd->channel_head[0].chip_head[0].die_head[0].plane_head[0].free_page - avg_write_amount * 0.3);
    ssd->parameter->gc_hard_threshold = new_gc_hard / (ssd->parameter->page_block * ssd->parameter->block_plane);
  }
  printf("gc hard threshold=%lf  free page:%lf", ssd->parameter->gc_hard_threshold, ssd->parameter->page_block * ssd->parameter->block_plane * ssd->parameter->gc_hard_threshold);
  fprintf(ssd->outputfile, "\t\t\t\t\t\t\t\t\tOUTPUT\n");
  fprintf(ssd->outputfile, "****************** TRACE INFO ******************\n");
  if (ssd == NULL) {
    printf("error\n");
  }
  ssd = simulate(ssd);
  printf("zero sub:%d sum sub:%d\n", ssd->gc_read_sub_zero_cnt, ssd->gc_read_sub_sum_cnt);
  printf("sum:%d lsb num:%d prob num %d\n", ssd->alloc_sum, ssd->alloc_lsb_nums, ssd->alloc_lsb_nums_for_prob);
  statistic_output(ssd);
  /*	free_all_node(ssd);*/

  printf("\n");
  printf("the simulation is completed!\n");
  printf("gc extr num is %d\n", gc_extra_num);
  printf("second choice is %d\n", second_choice_num);
  printf("third choice is %d\n", third_choice_num);
  return 1;
  /* 	_CrtDumpMemoryLeaks(); */
}

/******************simulate()
 **********************************************************************
 *simulate()是核心处理函数，主要实现的功能包括
 *1,从trace文件中获取一条请求，挂到ssd->request
 *2，根据ssd是否有dram分别处理读出来的请求，把这些请求处理成为读写子请求，挂到ssd->channel或者ssd上
 *3，按照事件的先后来处理这些读写子请求。
 *4，输出每条请求的子请求都处理完后的相关信息到outputfile文件中
 **************************************************************************************************/
struct ssd_info *simulate(struct ssd_info *ssd) {
  int flag = 1, flag1 = 0;
  double output_step = 0;
  unsigned int a = 0, b = 0;
  // errno_t err;

  printf("\n");
  printf("begin simulating.......................\n");
  printf("\n");
  printf("\n");
  printf(
      "   ^o^    OK, please wait a moment, and enjoy music and coffee   ^o^    "
      "\n");

  ssd->tracefile = fopen(ssd->tracefilename, "r");
  if (ssd->tracefile == NULL) {
    printf("the trace file can't open\n");
    return NULL;
  }

  fprintf(ssd->outputfile,
          "      arrive           lsn     size ope     begin time    response "
          "time    process time\n");
  fflush(ssd->outputfile);

  while (flag != 100) {
    flag = get_requests(ssd);

    if (flag == 1) {
      // printf("once\n");
      if (ssd->parameter->dram_capacity != 0) {
        buffer_management(ssd);
        distribute(ssd);
      } else {
        no_buffer_distribute(ssd);
      }
      ssd->avg_queue_length = (ssd->avg_queue_length * ssd->queue_length_cnt + ssd->request_queue_length) / (ssd->queue_length_cnt + 1);
      ssd->queue_length_cnt++;
    }

    process(ssd);
    trace_output(ssd);
    if (flag == 0 && ssd->request_queue == NULL) flag = 100;
  }

  fclose(ssd->tracefile);
  return ssd;
}

/********    get_request ******************************************************
 *	1.get requests that arrived already
 *	2.add those request node to ssd->reuqest_queue
 *	return	0: reach the end of the trace
 *			-1: no request has been added
 *			1: add one request to list
 *SSD模拟器有三种驱动方式:时钟驱动(精确，太慢) 事件驱动(本程序采用)
 *trace驱动()， 两种方式推进事件：channel/chip状态改变、trace文件请求达到。
 *channel/chip状态改变和trace文件请求到达是散布在时间轴上的点，每次从当前状态到达
 *下一个状态都要到达最近的一个状态，每到达一个点执行一次process
 ********************************************************************************/
int get_requests(struct ssd_info *ssd) {
  char buffer[20000];
  unsigned int lsn = 0;
  int device, size, ope, large_lsn, i = 0, j = 0;
  struct request *request1;
  int flag = 1;
  long filepoint;
  int64_t time_t = 0;
  int64_t nearest_event_time;
  int result;

#ifdef DEBUG
  printf("enter get_requests,  current time:%lld\n", ssd->current_time);
#endif

  if (feof(ssd->tracefile)) {
    nearest_event_time = find_nearest_event(ssd);
    if (nearest_event_time != MAX_INT64) {
      ssd->current_time = nearest_event_time;
    }
    return 0;
  }

  filepoint = ftell(ssd->tracefile);
  fgets(buffer, 20000, ssd->tracefile);
  // sscanf(buffer,"%lld %d %d %d %d",&time_t,&device,&lsn,&size,&ope);
  device = 0;
  result = read_data(buffer, &time_t, &lsn, &size, &ope);
  if ((device < 0) && (lsn < 0) && (size < 0) && (ope < 0)) {
    return 100;
  }
  if (lsn < ssd->min_lsn) ssd->min_lsn = lsn;
  if (lsn > ssd->max_lsn) ssd->max_lsn = lsn;
  /******************************************************************************************************
   *上层文件系统发送给SSD的任何读写命令包括两个部分（LSN，size）
   *LSN是逻辑扇区号，对于文件系统而言，它所看到的存
   *储空间是一个线性的连续空间。例如，读请求（260，6）表示的是需要读取从扇区号为260的逻辑扇区开始，总共6个扇区。
   *large_lsn:
   *channel下面有多少个subpage，即多少个sector。overprovide系数：SSD中并不是所有的空间都可以给用户使用，
   *比如32G的SSD可能有10%的空间保留下来留作他用，所以乘以1-provide
   ***********************************************************************************************************/
  large_lsn = (int)((ssd->parameter->subpage_page * ssd->parameter->page_block * ssd->parameter->block_plane * ssd->parameter->plane_die * ssd->parameter->die_chip * ssd->parameter->chip_num) *
                    (1 - ssd->parameter->overprovide));
  lsn = lsn % large_lsn;

  nearest_event_time = find_nearest_event(ssd);
  if (nearest_event_time == MAX_INT64) {
    ssd->current_time = time_t;

    // if (ssd->request_queue_length>ssd->parameter->queue_length)
    // //如果请求队列的长度超过了配置文件中所设置的长度
    //{
    // printf("error in get request , the queue length is too long\n");
    // }
  } else {
    if (nearest_event_time < time_t) {
      /*******************************************************************************
       *回滚，即如果没有把time_t赋给ssd->current_time，则trace文件已读的一条记录回滚
       *filepoint记录了执行fgets之前的文件指针位置，回滚到文件头+filepoint处
       *int fseek(FILE *stream, long offset, int
       *fromwhere);函数设置文件指针stream的位置。
       *如果执行成功，stream将指向以fromwhere（偏移起始位置：文件头0，当前位置1，文件尾2）为基准，
       *偏移offset（指针偏移量）个字节的位置。如果执行失败(比如offset超过文件自身大小)，则不改变stream指向的位置。
       *文本文件只能采用文件头0的定位方式，本程序中打开文件方式是"r":以只读方式打开文本文件
       **********************************************************************************/
      fseek(ssd->tracefile, filepoint, 0);
      if (ssd->current_time <= nearest_event_time) ssd->current_time = nearest_event_time;
      return -1;
    } else {
      if (ssd->request_queue_length >= ssd->parameter->queue_length) {
        fseek(ssd->tracefile, filepoint, 0);
        ssd->current_time = nearest_event_time;
        return -1;
      } else  //设计缺陷：如果请求第一次因为队列满而未进入设备，第二次进入设备时到达时间应该为上一次请求的完成时间，而这里时间未变，故设备队列无效
      {
        ssd->current_time = time_t;
      }
    }
  }

  if (time_t < 0) {
    printf("error!\n");
    while (1) {
    }
  }

  if (feof(ssd->tracefile)) {
    request1 = NULL;
    return 0;
  }

  request1 = (struct request *)malloc(sizeof(struct request));
  alloc_assert(request1, "request");
  memset(request1, 0, sizeof(struct request));

  request1->time = time_t;
  request1->lsn = lsn;
  request1->size = size;
  request1->operation = ope;
  request1->begin_time = time_t;
  request1->response_time = 0;
  request1->energy_consumption = 0;
  request1->next_node = NULL;
  request1->distri_flag = 0;  // indicate whether this request has been distributed already
  request1->subs = NULL;
  request1->need_distr_flag = NULL;
  request1->complete_lsn_count = 0;   // record the count of lsn served by buffer
  filepoint = ftell(ssd->tracefile);  // set the file point

  if (ssd->request_queue == NULL)  // The queue is empty
  {
    ssd->request_queue = request1;
    ssd->request_tail = request1;
    ssd->request_queue_length++;
  } else {
    (ssd->request_tail)->next_node = request1;
    ssd->request_tail = request1;
    ssd->request_queue_length++;
  }

  if (request1->operation == 1)  //计算平均请求大小 1为读 0为写
  {
    ssd->ave_read_size = (ssd->ave_read_size * ssd->read_request_count + request1->size) / (ssd->read_request_count + 1);
  } else {
    ssd->ave_write_size = (ssd->ave_write_size * ssd->write_request_count + request1->size) / (ssd->write_request_count + 1);
  }

  // filepoint = ftell(ssd->tracefile);
  // fgets(buffer, 200, ssd->tracefile); //寻找下一条请求的到达时间
  // //sscanf(buffer,"%lld %d %d %d %d",&time_t,&device,&lsn,&size,&ope);
  // device = 0;
  // result = read_data(buffer, &time_t, &lsn, &size, &ope);
  // ssd->next_request_time = time_t;
  // fseek(ssd->tracefile, filepoint, 0);

  return 1;
}

/**********************************************************************************************************************************************
 *首先buffer是个写buffer，就是为写请求服务的，因为读flash的时间tR为20us，写flash的时间tprog为200us，所以为写服务更能节省时间
 *  读操作：如果命中了buffer，从buffer读，不占用channel的I/O总线，没有命中buffer，从flash读，占用channel的I/O总线，但是不进buffer了
 *  写操作：首先request分成sub_request子请求，如果是动态分配，sub_request挂到ssd->sub_request上，因为不知道要先挂到哪个channel的sub_request上
 *          如果是静态分配则sub_request挂到channel的sub_request链上,同时不管动态分配还是静态分配sub_request都要挂到request的sub_request链上
 *		   因为每处理完一个request，都要在traceoutput文件中输出关于这个request的信息。处理完一个sub_request,就将其从channel的sub_request链
 *		   或ssd的sub_request链上摘除，但是在traceoutput文件输出一条后再清空request的sub_request链。
 *		   sub_request命中buffer则在buffer里面写就行了，并且将该sub_page提到buffer链头(LRU)，若没有命中且buffer满，则先将buffer链尾的sub_request
 *		   写入flash(这会产生一个sub_request写请求，挂到这个请求request的sub_request链上，同时视动态分配还是静态分配挂到channel或ssd的
 *		   sub_request链上),在将要写的sub_page写入buffer链头
 ***********************************************************************************************************************************************/

struct ssd_info *buffer_management(struct ssd_info *ssd) {
  unsigned int j, lsn, lpn, last_lpn, first_lpn, index, complete_flag = 0, state, full_page;
  unsigned int flag = 0, need_distb_flag, lsn_flag, flag1 = 1, active_region_flag = 0;
  struct request *new_request;
  struct buffer_group *buffer_node, key;
  unsigned int mask = 0, offset1 = 0, offset2 = 0;

#ifdef DEBUG
  printf("enter buffer_management,  current time:%lld\n", ssd->current_time);
#endif
  ssd->dram->current_time = ssd->current_time;
  full_page = ~(0xffffffff << ssd->parameter->subpage_page);

  new_request = ssd->request_tail;
  lsn = new_request->lsn;
  lpn = new_request->lsn / ssd->parameter->subpage_page;
  last_lpn = (new_request->lsn + new_request->size - 1) / ssd->parameter->subpage_page;
  first_lpn = new_request->lsn / ssd->parameter->subpage_page;

  /*************************************************************************************
  *一个32位的整型数据的每一位代表一个子页，32/ssd->parameter->subpage_page就表示有多少页，
  *这里的每一页的状态都存放在了
  req->need_distr_flag中，也就是complt中，通过比较complt的
  *每一项与full_page，就可以知道，这一页是否处理完成。如果没处理完成则通过creat_sub_request
  函数创建子请求。
  *************************************************************************************/
  new_request->need_distr_flag = (unsigned int *)malloc(sizeof(unsigned int) * ((last_lpn - first_lpn + 1) * ssd->parameter->subpage_page / 32 + 1));
  alloc_assert(new_request->need_distr_flag, "new_request->need_distr_flag");
  memset(new_request->need_distr_flag, 0, sizeof(unsigned int) * ((last_lpn - first_lpn + 1) * ssd->parameter->subpage_page / 32 + 1));

  if (new_request->operation == READ) {
    while (lpn <= last_lpn) {
      /************************************************************************************************
       *need_distb_flag表示是否需要执行distribution函数，1表示需要执行，buffer中没有，0表示不需要执行
       *即1表示需要分发，0表示不需要分发，对应点初始全部赋为1
       *************************************************************************************************/
      need_distb_flag = full_page;
      key.group = lpn;
      buffer_node = (struct buffer_group *)avlTreeFind(ssd->dram->buffer, (TREE_NODE *)&key);  // buffer node

      while ((buffer_node != NULL) && (lsn < (lpn + 1) * ssd->parameter->subpage_page) && (lsn <= (new_request->lsn + new_request->size - 1))) {
        lsn_flag = full_page;
        mask = 1 << (lsn % ssd->parameter->subpage_page);
        if (mask > 32 - 1) {
          printf("the subpage number is larger than 32!add some cases");
          getchar();
        } else if ((buffer_node->stored & mask) == mask) {
          flag = 1;
          lsn_flag = lsn_flag & (~mask);
        }

        if (flag == 1) {  //如果该buffer节点不在buffer的队首，需要将这个节点提到队首，实现了LRU算法，这个是一个双向队列。
          if (ssd->dram->buffer->buffer_head != buffer_node) {
            if (ssd->dram->buffer->buffer_tail == buffer_node) {
              buffer_node->LRU_link_pre->LRU_link_next = NULL;
              ssd->dram->buffer->buffer_tail = buffer_node->LRU_link_pre;
            } else {
              buffer_node->LRU_link_pre->LRU_link_next = buffer_node->LRU_link_next;
              buffer_node->LRU_link_next->LRU_link_pre = buffer_node->LRU_link_pre;
            }
            buffer_node->LRU_link_next = ssd->dram->buffer->buffer_head;
            ssd->dram->buffer->buffer_head->LRU_link_pre = buffer_node;
            buffer_node->LRU_link_pre = NULL;
            ssd->dram->buffer->buffer_head = buffer_node;
          }
          ssd->dram->buffer->read_hit++;
          new_request->complete_lsn_count++;
        } else if (flag == 0) {
          ssd->dram->buffer->read_miss_hit++;
        }

        need_distb_flag = need_distb_flag & lsn_flag;

        flag = 0;
        lsn++;
      }

      index = (lpn - first_lpn) / (32 / ssd->parameter->subpage_page);
      new_request->need_distr_flag[index] = new_request->need_distr_flag[index] | (need_distb_flag << (((lpn - first_lpn) % (32 / ssd->parameter->subpage_page)) * ssd->parameter->subpage_page));
      lpn++;
    }
  } else if (new_request->operation == WRITE) {
    while (lpn <= last_lpn) {
      need_distb_flag = full_page;
      mask = ~(0xffffffff << (ssd->parameter->subpage_page));
      state = mask;

      if (lpn == first_lpn) {
        offset1 = ssd->parameter->subpage_page - ((lpn + 1) * ssd->parameter->subpage_page - new_request->lsn);
        state = state & (0xffffffff << offset1);
      }
      if (lpn == last_lpn) {
        offset2 = ssd->parameter->subpage_page - ((lpn + 1) * ssd->parameter->subpage_page - (new_request->lsn + new_request->size));
        state = state & (~(0xffffffff << offset2));
      }

      ssd = insert2buffer(ssd, lpn, state, NULL, new_request);
      lpn++;
    }
  }
  complete_flag = 1;
  for (j = 0; j <= (last_lpn - first_lpn + 1) * ssd->parameter->subpage_page / 32; j++) {
    if (new_request->need_distr_flag[j] != 0) {
      complete_flag = 0;
    }
  }

  /*************************************************************
   *如果请求已经被全部由buffer服务，该请求可以被直接响应，输出结果
   *这里假设dram的服务时间为1000ns
   **************************************************************/
  if ((complete_flag == 1) && (new_request->subs == NULL)) {
    new_request->begin_time = ssd->current_time;
    new_request->response_time = ssd->current_time + 1000;
  }

  return ssd;
}

/*****************************
 *lpn向ppn的转换
 ******************************/
unsigned int lpn2ppn(struct ssd_info *ssd, unsigned int lsn) {
  int lpn, ppn;
  struct entry *p_map = ssd->dram->map->map_entry;
#ifdef DEBUG
  printf("enter lpn2ppn,  current time:%lld\n", ssd->current_time);
#endif
  lpn = lsn / ssd->parameter->subpage_page;  // lpn
  ppn = (p_map[lpn]).pn;
  return ppn;
}

/**********************************************************************************
 *读请求分配子请求函数，这里只处理读请求，写请求已经在buffer_management()函数中处理了
 *根据请求队列和buffer命中的检查，将每个请求分解成子请求，将子请求队列挂在channel上，
 *不同的channel有自己的子请求队列
 **********************************************************************************/

struct ssd_info *distribute(struct ssd_info *ssd) {
  unsigned int start, end, first_lsn, last_lsn, lpn, flag = 0, flag_attached = 0, full_page;
  unsigned int j, k, sub_size;
  int i = 0;
  struct request *req;
  struct sub_request *sub;
  int *complt;

#ifdef DEBUG
  printf("enter distribute,  current time:%lld\n", ssd->current_time);
#endif
  full_page = ~(0xffffffff << ssd->parameter->subpage_page);

  req = ssd->request_tail;
  if (req->response_time != 0) {
    return ssd;
  }
  if (req->operation == WRITE) {
    return ssd;
  }

  if (req != NULL) {
    if (req->distri_flag == 0) {
      //如果还有一些读请求需要处理
      if (req->complete_lsn_count != ssd->request_tail->size) {
        first_lsn = req->lsn;
        last_lsn = first_lsn + req->size;
        complt = req->need_distr_flag;
        start = first_lsn - first_lsn % ssd->parameter->subpage_page;
        end = (last_lsn / ssd->parameter->subpage_page + 1) * ssd->parameter->subpage_page;
        i = (end - start) / 32;

        while (i >= 0) {
          /*************************************************************************************
           *一个32位的整型数据的每一位代表一个子页，32/ssd->parameter->subpage_page就表示有多少页，
           *这里的每一页的状态都存放在了
           req->need_distr_flag中，也就是complt中，通过比较complt的
           *每一项与full_page，就可以知道，这一页是否处理完成。如果没处理完成则通过creat_sub_request
           函数创建子请求。
           *************************************************************************************/
          for (j = 0; j < 32 / ssd->parameter->subpage_page; j++) {
            k = (complt[((end - start) / 32 - i)] >> (ssd->parameter->subpage_page * j)) & full_page;
            if (k != 0) {
              lpn = start / ssd->parameter->subpage_page + ((end - start) / 32 - i) * 32 / ssd->parameter->subpage_page + j;
              sub_size = transfer_size(ssd, k, lpn, req);
              if (sub_size == 0) {
                continue;
              } else {
                sub = creat_sub_request(ssd, lpn, sub_size, 0, req, req->operation);
              }
            }
          }
          i = i - 1;
        }
      } else {
        req->begin_time = ssd->current_time;
        req->response_time = ssd->current_time + 1000;
      }
    }
  }
  return ssd;
}

/**********************************************************************
 *trace_output()函数是在每一条请求的所有子请求经过process()函数处理完后，
 *打印输出相关的运行结果到outputfile文件中，这里的结果主要是运行的时间
 **********************************************************************/
void trace_output(struct ssd_info *ssd) {
  int flag = 1;
  int64_t start_time, end_time;
  struct request *req, *pre_node;
  struct sub_request *sub, *tmp;

#ifdef DEBUG
  printf("enter trace_output,  current time:%lld\n", ssd->current_time);
#endif

  pre_node = NULL;
  req = ssd->request_queue;
  start_time = 0;
  end_time = 0;

  if (req == NULL) return;

  while (req != NULL) {
    sub = req->subs;
    flag = 1;
    start_time = 0;
    end_time = 0;
    if (req->response_time != 0) {
      fprintf(ssd->outputfile, "%16lld %10d %6d %2d %16lld %16lld %10lld\n", req->time, req->lsn, req->size, req->operation, req->begin_time, req->response_time, req->response_time - req->begin_time);
      fflush(ssd->outputfile);

      if (req->response_time - req->begin_time == 0) {
        printf("the response time is 0?? \n");
        getchar();
      }

      if (req->operation == READ) {
        ssd->read_request_count++;
        ssd->read_avg = ssd->read_avg + (req->response_time - req->begin_time);
      } else {
        ssd->write_request_count++;
        ssd->write_avg = ssd->write_avg + (req->response_time - req->begin_time);
      }

      if (pre_node == NULL) {
        if (req->next_node == NULL) {
          free(req->need_distr_flag);
          req->need_distr_flag = NULL;
          free(req);
          req = NULL;
          ssd->request_queue = NULL;
          ssd->request_tail = NULL;
          ssd->request_queue_length--;
        } else {
          ssd->request_queue = req->next_node;
          pre_node = req;
          req = req->next_node;
          free(pre_node->need_distr_flag);
          pre_node->need_distr_flag = NULL;
          free((void *)pre_node);
          pre_node = NULL;
          ssd->request_queue_length--;
        }
      } else {
        if (req->next_node == NULL) {
          pre_node->next_node = NULL;
          free(req->need_distr_flag);
          req->need_distr_flag = NULL;
          free(req);
          req = NULL;
          ssd->request_tail = pre_node;
          ssd->request_queue_length--;
        } else {
          pre_node->next_node = req->next_node;
          free(req->need_distr_flag);
          req->need_distr_flag = NULL;
          free((void *)req);
          req = pre_node->next_node;
          ssd->request_queue_length--;
        }
      }
    } else {
      flag = 1;
      while (sub != NULL) {
        if (start_time == 0) start_time = sub->begin_time;
        if (start_time > sub->begin_time) start_time = sub->begin_time;
        if (end_time < sub->complete_time) end_time = sub->complete_time;
        if ((sub->current_state == SR_COMPLETE) || ((sub->next_state == SR_COMPLETE) && (sub->next_state_predict_time <= ssd->current_time)))  // if any sub-request is not completed, the
                                                                                                                                               // request is not completed
        {
          sub = sub->next_subs;
        } else {
          flag = 0;
          break;
        }
      }

      if (flag == 1) {
        // fprintf(ssd->outputfile,"%10I64u %10u %6u %2u %16I64u %16I64u
        // %10I64u\n",req->time,req->lsn, req->size, req->operation, start_time,
        // end_time, end_time-req->time);
        fprintf(ssd->outputfile, "%16lld %10d %6d %2d %16lld %16lld %10lld\n", req->time, req->lsn, req->size, req->operation, start_time, end_time, end_time - req->begin_time);
        fflush(ssd->outputfile);

        if (end_time - start_time == 0) {
          printf("the response time is 0?? \n");
          getchar();
        }

        if (req->operation == READ) {
          ssd->read_request_count++;
          ssd->read_avg = ssd->read_avg + (end_time - start_time);
        } else {
          ssd->write_request_count++;
          ssd->write_avg = ssd->write_avg + (end_time - start_time);
        }

        while (req->subs != NULL) {
          tmp = req->subs;
          req->subs = tmp->next_subs;
          if (tmp->update != NULL) {
            free(tmp->update->location);
            tmp->update->location = NULL;
            free(tmp->update);
            tmp->update = NULL;
          }
          free(tmp->location);
          tmp->location = NULL;
          free(tmp);
          tmp = NULL;
        }

        if (pre_node == NULL) {
          if (req->next_node == NULL) {
            free(req->need_distr_flag);
            req->need_distr_flag = NULL;
            free(req);
            req = NULL;
            ssd->request_queue = NULL;
            ssd->request_tail = NULL;
            ssd->request_queue_length--;
          } else {
            ssd->request_queue = req->next_node;
            pre_node = req;
            req = req->next_node;
            free(pre_node->need_distr_flag);
            pre_node->need_distr_flag = NULL;
            free(pre_node);
            pre_node = NULL;
            ssd->request_queue_length--;
          }
        } else {
          if (req->next_node == NULL) {
            pre_node->next_node = NULL;
            free(req->need_distr_flag);
            req->need_distr_flag = NULL;
            free(req);
            req = NULL;
            ssd->request_tail = pre_node;
            ssd->request_queue_length--;
          } else {
            pre_node->next_node = req->next_node;
            free(req->need_distr_flag);
            req->need_distr_flag = NULL;
            free(req);
            req = pre_node->next_node;
            ssd->request_queue_length--;
          }
        }
      } else {
        pre_node = req;
        req = req->next_node;
      }
    }
  }
}

/*******************************************************************************
 *statistic_output()函数主要是输出处理完一条请求后的相关处理信息。
 *1，计算出每个plane的擦除次数即plane_erase和总的擦除次数即erase
 *2，打印min_lsn，max_lsn，read_count，program_count等统计信息到文件outputfile中。
 *3，打印相同的信息到文件statisticfile中
 *******************************************************************************/
void statistic_output(struct ssd_info *ssd) {
  unsigned int lpn_count = 0, i, j, k, m, erase = 0, plane_erase = 0;
  double gc_energy = 0.0;
#ifdef DEBUG
  printf("enter statistic_output,  current time:%lld\n", ssd->current_time);
#endif

  for (i = 0; i < ssd->parameter->channel_number; i++) {
    for (j = 0; j < ssd->parameter->die_chip; j++) {
      for (k = 0; k < ssd->parameter->plane_die; k++) {
        plane_erase = 0;
        for (m = 0; m < ssd->parameter->block_plane; m++) {
          if (ssd->channel_head[i].chip_head[0].die_head[j].plane_head[k].blk_head[m].erase_count > 0) {
            erase = erase + ssd->channel_head[i].chip_head[0].die_head[j].plane_head[k].blk_head[m].erase_count;
            plane_erase += ssd->channel_head[i].chip_head[0].die_head[j].plane_head[k].blk_head[m].erase_count;
          }
        }
        fprintf(ssd->outputfile,
                "the %d channel, 0 chip, %d die, %d plane has : %13d erase "
                "operations\n",
                i, j, k, plane_erase);
        fprintf(ssd->statisticfile,
                "the %d channel, 0 chip, %d die, %d plane has : %13d erase "
                "operations\n",
                i, j, k, plane_erase);
      }
    }
  }

  fprintf(ssd->outputfile, "\n");
  fprintf(ssd->outputfile, "\n");
  fprintf(ssd->outputfile, "---------------------------statistic data---------------------------\n");
  fprintf(ssd->outputfile, "min lsn: %13d\n", ssd->min_lsn);
  fprintf(ssd->outputfile, "max lsn: %13d\n", ssd->max_lsn);
  fprintf(ssd->outputfile, "read count: %13d\n", ssd->read_count);
  fprintf(ssd->outputfile, "program count: %13d", ssd->program_count);
  fprintf(ssd->outputfile,
          "                        include the flash write count leaded by "
          "read requests\n");
  fprintf(ssd->outputfile, "the read operation leaded by un-covered update count: %13d\n", ssd->update_read_count);
  fprintf(ssd->outputfile, "erase count: %13d\n", ssd->erase_count);
  fprintf(ssd->outputfile, "direct erase count: %13d\n", ssd->direct_erase_count);
  fprintf(ssd->outputfile, "copy back count: %13d\n", ssd->copy_back_count);
  fprintf(ssd->outputfile, "multi-plane program count: %13d\n", ssd->m_plane_prog_count);
  fprintf(ssd->outputfile, "multi-plane read count: %13d\n", ssd->m_plane_read_count);
  fprintf(ssd->outputfile, "interleave write count: %13d\n", ssd->interleave_count);
  fprintf(ssd->outputfile, "interleave read count: %13d\n", ssd->interleave_read_count);
  fprintf(ssd->outputfile, "interleave two plane and one program count: %13d\n", ssd->inter_mplane_prog_count);
  fprintf(ssd->outputfile, "interleave two plane count: %13d\n", ssd->inter_mplane_count);
  fprintf(ssd->outputfile, "gc copy back count: %13d\n", ssd->gc_copy_back);
  fprintf(ssd->outputfile, "write flash count: %13d\n", ssd->write_flash_count);
  fprintf(ssd->outputfile, "interleave erase count: %13d\n", ssd->interleave_erase_count);
  fprintf(ssd->outputfile, "multiple plane erase count: %13d\n", ssd->mplane_erase_conut);
  fprintf(ssd->outputfile, "interleave multiple plane erase count: %13d\n", ssd->interleave_mplane_erase_count);
  fprintf(ssd->outputfile, "read request count: %13d\n", ssd->read_request_count);
  fprintf(ssd->outputfile, "write request count: %13d\n", ssd->write_request_count);
  fprintf(ssd->outputfile, "read request average size: %13f\n", ssd->ave_read_size);
  fprintf(ssd->outputfile, "write request average size: %13f\n", ssd->ave_write_size);
  if (ssd->read_request_count != 0) {
    fprintf(ssd->outputfile, "read request average response time: %lld\n", ssd->read_avg / ssd->read_request_count);
  } else {
    printf("read count=0\n");
  }
  if (ssd->write_request_count != 0) {
    fprintf(ssd->outputfile, "write request average response time: %lld\n", ssd->write_avg / ssd->write_request_count);
  } else {
    printf("write count=0\n");
  }
  fprintf(ssd->outputfile, "buffer read hits: %13d\n", ssd->dram->buffer->read_hit);
  fprintf(ssd->outputfile, "buffer read miss: %13d\n", ssd->dram->buffer->read_miss_hit);
  fprintf(ssd->outputfile, "buffer write hits: %13d\n", ssd->dram->buffer->write_hit);
  fprintf(ssd->outputfile, "buffer write miss: %13d\n", ssd->dram->buffer->write_miss_hit);
  fprintf(ssd->outputfile, "erase: %13d\n", erase);
  fflush(ssd->outputfile);

  fclose(ssd->outputfile);

  fprintf(ssd->statisticfile, "\n");
  fprintf(ssd->statisticfile, "\n");
  fprintf(ssd->statisticfile, "---------------------------statistic data---------------------------\n");
  fprintf(ssd->statisticfile, "min lsn: %13d\n", ssd->min_lsn);
  fprintf(ssd->statisticfile, "max lsn: %13d\n", ssd->max_lsn);
  fprintf(ssd->statisticfile, "read count: %13d\n", ssd->read_count);
  fprintf(ssd->statisticfile, "program count: %13d", ssd->program_count);
  fprintf(ssd->statisticfile,
          "                        include the flash write count leaded by "
          "read requests\n");
  fprintf(ssd->statisticfile, "the read operation leaded by un-covered update count: %13d\n", ssd->update_read_count);
  fprintf(ssd->statisticfile, "erase count: %13d\n", ssd->erase_count);
  fprintf(ssd->statisticfile, "direct erase count: %13d\n", ssd->direct_erase_count);
  fprintf(ssd->statisticfile, "copy back count: %13d\n", ssd->copy_back_count);
  fprintf(ssd->statisticfile, "multi-plane program count: %13d\n", ssd->m_plane_prog_count);
  fprintf(ssd->statisticfile, "multi-plane read count: %13d\n", ssd->m_plane_read_count);
  fprintf(ssd->statisticfile, "interleave count: %13d\n", ssd->interleave_count);
  fprintf(ssd->statisticfile, "interleave read count: %13d\n", ssd->interleave_read_count);
  fprintf(ssd->statisticfile, "interleave two plane and one program count: %13d\n", ssd->inter_mplane_prog_count);
  fprintf(ssd->statisticfile, "interleave two plane count: %13d\n", ssd->inter_mplane_count);
  fprintf(ssd->statisticfile, "gc copy back count: %13d\n", ssd->gc_copy_back);
  fprintf(ssd->statisticfile, "write flash count: %13d\n", ssd->write_flash_count);
  fprintf(ssd->statisticfile, "waste page count: %13d\n", ssd->waste_page_count);
  fprintf(ssd->statisticfile, "interleave erase count: %13d\n", ssd->interleave_erase_count);
  fprintf(ssd->statisticfile, "multiple plane erase count: %13d\n", ssd->mplane_erase_conut);
  fprintf(ssd->statisticfile, "interleave multiple plane erase count: %13d\n", ssd->interleave_mplane_erase_count);
  fprintf(ssd->statisticfile, "read request count: %13d\n", ssd->read_request_count);
  fprintf(ssd->statisticfile, "write request count: %13d\n", ssd->write_request_count);
  fprintf(ssd->statisticfile, "read request average size: %13f\n", ssd->ave_read_size);
  fprintf(ssd->statisticfile, "write request average size: %13f\n", ssd->ave_write_size);

  if (ssd->read_request_count != 0) {
    fprintf(ssd->statisticfile, "read request average response time: %lld\n", ssd->read_avg / ssd->read_request_count);
  } else {
    printf("read count=0\n");
  }
  if (ssd->write_request_count != 0) {
    fprintf(ssd->statisticfile, "write request average response time: %lld\n", ssd->write_avg / ssd->write_request_count);
  } else {
    printf("write count=0\n");
  }
  fprintf(ssd->statisticfile, "update write count: %lld\n", ssd->update_write_count);
  if (ssd->gc_count != 0) {
    fprintf(ssd->statisticfile, "gc average response time: %lld\n", ssd->gc_sum_time / ssd->gc_count);
    fprintf(ssd->statisticfile, "gc count: %13d\n", ssd->gc_count);
    fprintf(ssd->statisticfile, "gc average page move count: %13d\n", ssd->gc_page_move_count / ssd->gc_count);
  }
  // fprintf(ssd->statisticfile, "read request average response time: %lld\n",
  // ssd->read_avg / ssd->read_request_count);

  // fprintf(ssd->statisticfile, "write request average response time: %lld\n",
  // ssd->write_avg / ssd->write_request_count);

  fprintf(ssd->statisticfile, "buffer read hits: %13d\n", ssd->dram->buffer->read_hit);
  fprintf(ssd->statisticfile, "buffer read miss: %13d\n", ssd->dram->buffer->read_miss_hit);
  fprintf(ssd->statisticfile, "buffer write hits: %13d\n", ssd->dram->buffer->write_hit);
  fprintf(ssd->statisticfile, "buffer write miss: %13d\n", ssd->dram->buffer->write_miss_hit);
  fprintf(ssd->statisticfile, "erase: %13d\n", erase);
  fflush(ssd->statisticfile);

  fclose(ssd->statisticfile);
  //将主要数据输出至汇总文件
  fprintf(ssd->sumfile, "******%s******\n", ssd->tracefilename);
  fprintf(ssd->sumfile, "min lsn: %13d\n", ssd->min_lsn);
  fprintf(ssd->sumfile, "max lsn: %13d\n", ssd->max_lsn);
  fprintf(ssd->sumfile, "read count: %13d\n", ssd->read_count);
  fprintf(ssd->sumfile, "program count: %13d", ssd->program_count);
  fprintf(ssd->sumfile,
          "                        include the flash write count leaded by "
          "read requests\n");
  fprintf(ssd->sumfile, "the read operation leaded by un-covered update count: %13d\n", ssd->update_read_count);
  fprintf(ssd->sumfile, "erase count: %13d\n", ssd->erase_count);
  fprintf(ssd->sumfile, "direct erase count: %13d\n", ssd->direct_erase_count);
  fprintf(ssd->sumfile, "copy back count: %13d\n", ssd->copy_back_count);
  fprintf(ssd->sumfile, "multi-plane program count: %13d\n", ssd->m_plane_prog_count);
  fprintf(ssd->sumfile, "multi-plane read count: %13d\n", ssd->m_plane_read_count);
  fprintf(ssd->sumfile, "interleave count: %13d\n", ssd->interleave_count);
  fprintf(ssd->sumfile, "interleave read count: %13d\n", ssd->interleave_read_count);
  fprintf(ssd->sumfile, "interleave two plane and one program count: %13d\n", ssd->inter_mplane_prog_count);
  fprintf(ssd->sumfile, "interleave two plane count: %13d\n", ssd->inter_mplane_count);
  fprintf(ssd->sumfile, "gc copy back count: %13d\n", ssd->gc_copy_back);
  fprintf(ssd->sumfile, "write flash count: %13d\n", ssd->write_flash_count);
  fprintf(ssd->sumfile, "waste page count: %13d\n", ssd->waste_page_count);
  fprintf(ssd->sumfile, "interleave erase count: %13d\n", ssd->interleave_erase_count);
  fprintf(ssd->sumfile, "multiple plane erase count: %13d\n", ssd->mplane_erase_conut);
  fprintf(ssd->sumfile, "interleave multiple plane erase count: %13d\n", ssd->interleave_mplane_erase_count);
  fprintf(ssd->sumfile, "read request count: %13d\n", ssd->read_request_count);
  fprintf(ssd->sumfile, "write request count: %13d\n", ssd->write_request_count);
  fprintf(ssd->sumfile, "read request average size: %13f\n", ssd->ave_read_size);
  fprintf(ssd->sumfile, "write request average size: %13f\n", ssd->ave_write_size);

  if (ssd->read_request_count != 0) {
    fprintf(ssd->sumfile, "read request average response time: %lld\n", ssd->read_avg / ssd->read_request_count);
  } else {
    printf("read count=0\n");
  }
  if (ssd->write_request_count != 0) {
    fprintf(ssd->sumfile, "write request average response time: %lld\n", ssd->write_avg / ssd->write_request_count);
  } else {
    printf("write count=0\n");
  }
  fprintf(ssd->sumfile, "update write count: %lld\n", ssd->update_write_count);
  if (ssd->gc_count != 0) {
    fprintf(ssd->sumfile, "gc average response time: %lld\n", ssd->gc_sum_time / ssd->gc_count);
    fprintf(ssd->sumfile, "page move time: %lld\n", ssd->gc_sum_time / ssd->gc_count - ssd->parameter->time_characteristics.tBERS);
    fprintf(ssd->sumfile, "block erase time: %lld\n", ssd->parameter->time_characteristics.tBERS);
    fprintf(ssd->sumfile, "gc count: %13d\n", ssd->gc_count);
    fprintf(ssd->sumfile, "gc average page move count: %13d\n", ssd->gc_page_move_count / ssd->gc_count);
  }
  // fprintf(ssd->sumfile, "read request average response time: %lld\n",
  // ssd->read_avg / ssd->read_request_count);

  // fprintf(ssd->sumfile, "write request average response time: %lld\n",
  // ssd->write_avg / ssd->write_request_count);

  fprintf(ssd->sumfile, "buffer read hits: %13d\n", ssd->dram->buffer->read_hit);
  fprintf(ssd->sumfile, "buffer read miss: %13d\n", ssd->dram->buffer->read_miss_hit);
  fprintf(ssd->sumfile, "buffer write hits: %13d\n", ssd->dram->buffer->write_hit);
  fprintf(ssd->sumfile, "buffer write miss: %13d\n", ssd->dram->buffer->write_miss_hit);
  fprintf(ssd->sumfile, "erase: %13d\n", erase);
  fflush(ssd->sumfile);
  fclose(ssd->sumfile);
}

/***********************************************************************************
 *根据每一页的状态计算出每一需要处理的子页的数目，也就是一个子请求需要处理的子页的页数
 ************************************************************************************/
unsigned int size(unsigned int stored) {
  unsigned int i, total = 0, mask = 0x80000000;

#ifdef DEBUG
  printf("enter size\n");
#endif
  for (i = 1; i <= 32; i++) {
    if (stored & mask) total++;
    stored <<= 1;
  }
#ifdef DEBUG
  printf("leave size\n");
#endif
  return total;
}

/*********************************************************
 *transfer_size()函数的作用就是计算出子请求的需要处理的size
 *函数中单独处理了first_lpn，last_lpn这两个特别情况，因为这
 *两种情况下很有可能不是处理一整页而是处理一页的一部分，因
 *为lsn有可能不是一页的第一个子页。
 *********************************************************/
unsigned int transfer_size(struct ssd_info *ssd, int need_distribute, unsigned int lpn, struct request *req) {
  unsigned int first_lpn, last_lpn, state, trans_size;
  unsigned int mask = 0, offset1 = 0, offset2 = 0;

  first_lpn = req->lsn / ssd->parameter->subpage_page;
  last_lpn = (req->lsn + req->size - 1) / ssd->parameter->subpage_page;

  mask = ~(0xffffffff << (ssd->parameter->subpage_page));
  state = mask;
  if (lpn == first_lpn) {
    offset1 = ssd->parameter->subpage_page - ((lpn + 1) * ssd->parameter->subpage_page - req->lsn);
    state = state & (0xffffffff << offset1);
  }
  if (lpn == last_lpn) {
    offset2 = ssd->parameter->subpage_page - ((lpn + 1) * ssd->parameter->subpage_page - (req->lsn + req->size));
    state = state & (~(0xffffffff << offset2));
  }

  trans_size = size(state & need_distribute);

  return trans_size;
}

/**********************************************************************************************************
 *int64_t find_nearest_event(struct ssd_info *ssd)
 *寻找所有子请求的最早到达的下个状态时间,首先看请求的下一个状态时间，如果请求的下个状态时间小于等于当前时间，
 *说明请求被阻塞，需要查看channel或者对应die的下一状态时间。Int64是有符号 64
 *位整数数据类型，值类型表示值介于 -2^63 (
 *-9,223,372,036,854,775,808)到2^63-1(+9,223,372,036,854,775,807
 *)之间的整数。存储空间占 8 字节。
 *channel,die是事件向前推进的关键因素，三种情况可以使事件继续向前推进，channel，die分别回到idle状态，die中的
 *读数据准备好了
 ***********************************************************************************************************/
int64_t find_nearest_event(struct ssd_info *ssd) {
  unsigned int i, j;
  int64_t time = MAX_INT64;
  int64_t time1 = MAX_INT64;
  int64_t time2 = MAX_INT64;

  for (i = 0; i < ssd->parameter->channel_number; i++) {
    if (ssd->channel_head[i].next_state == CHANNEL_IDLE)
      if (time1 > ssd->channel_head[i].next_state_predict_time)
        if (ssd->channel_head[i].next_state_predict_time > ssd->current_time) time1 = ssd->channel_head[i].next_state_predict_time;
    for (j = 0; j < ssd->parameter->chip_channel[i]; j++) {
      if ((ssd->channel_head[i].chip_head[j].next_state == CHIP_IDLE) || (ssd->channel_head[i].chip_head[j].next_state == CHIP_DATA_TRANSFER))
        if (time2 > ssd->channel_head[i].chip_head[j].next_state_predict_time)
          if (ssd->channel_head[i].chip_head[j].next_state_predict_time > ssd->current_time) time2 = ssd->channel_head[i].chip_head[j].next_state_predict_time;
    }
  }

  /*****************************************************************************************************
   *time为所有
   *A.下一状态为CHANNEL_IDLE且下一状态预计时间大于ssd当前时间的CHANNEL的下一状态预计时间
   *           B.下一状态为CHIP_IDLE且下一状态预计时间大于ssd当前时间的DIE的下一状态预计时间
   *		     C.下一状态为CHIP_DATA_TRANSFER且下一状态预计时间大于ssd当前时间的DIE的下一状态预计时间
   *CHIP_DATA_TRANSFER读准备好状态，数据已从介质传到了register，下一状态是从register传往buffer中的最小值
   *注意可能都没有满足要求的time，这时time返回0x7fffffffffffffff 。
   *****************************************************************************************************/
  time = (time1 > time2) ? time2 : time1;
  return time;
}

/***********************************************
 *free_all_node()函数的作用就是释放所有申请的节点
 ************************************************/
void free_all_node(struct ssd_info *ssd) {
  unsigned int i, j, k, l, n;
  struct buffer_group *pt = NULL;
  struct direct_erase *erase_node = NULL;
  for (i = 0; i < ssd->parameter->channel_number; i++) {
    for (j = 0; j < ssd->parameter->chip_channel[0]; j++) {
      for (k = 0; k < ssd->parameter->die_chip; k++) {
        for (l = 0; l < ssd->parameter->plane_die; l++) {
          for (n = 0; n < ssd->parameter->block_plane; n++) {
            free(ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head[n].page_head);
            ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head[n].page_head = NULL;
          }
          free(ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head);
          ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head = NULL;
          while (ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].erase_node != NULL) {
            erase_node = ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].erase_node;
            ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].erase_node = erase_node->next_node;
            free(erase_node);
            erase_node = NULL;
          }
        }

        free(ssd->channel_head[i].chip_head[j].die_head[k].plane_head);
        ssd->channel_head[i].chip_head[j].die_head[k].plane_head = NULL;
      }
      free(ssd->channel_head[i].chip_head[j].die_head);
      ssd->channel_head[i].chip_head[j].die_head = NULL;
    }
    free(ssd->channel_head[i].chip_head);
    ssd->channel_head[i].chip_head = NULL;
  }
  free(ssd->channel_head);
  ssd->channel_head = NULL;

  avlTreeDestroy(ssd->dram->buffer);
  ssd->dram->buffer = NULL;

  free(ssd->dram->map->map_entry);
  ssd->dram->map->map_entry = NULL;
  free(ssd->dram->map);
  ssd->dram->map = NULL;
  free(ssd->dram);
  ssd->dram = NULL;
  free(ssd->parameter);
  ssd->parameter = NULL;

  free(ssd);
  ssd = NULL;
}

/*****************************************************************************
 *make_aged()函数的作用就死模拟真实的用过一段时间的ssd，
 *那么这个ssd的相应的参数就要改变，所以这个函数实质上就是对ssd中各个参数的赋值。
 ******************************************************************************/
struct ssd_info *make_aged(struct ssd_info *ssd) {
  unsigned int i, j, k, l, m, n, ppn;
  int threshould, flag = 0;

  //一个块中的有效页数无效页占比 20%
  int vaild_page_ratio = 2;
  //有效页的逻辑页号
  int crt_lpn = 0;
  int lpn, valid_state;
  int rand_value;
  int large_lpn = (ssd->parameter->chip_num * ssd->parameter->die_chip * ssd->parameter->plane_die * ssd->parameter->block_plane * ssd->parameter->page_block) * (1 - ssd->parameter->overprovide);
  if (ssd->parameter->aged == 1) {
    // threshold表示一个plane中有多少页需要提前置为失效或有效
    threshould = (int)(ssd->parameter->block_plane * ssd->parameter->page_block * ssd->parameter->aged_ratio);
    for (i = 0; i < ssd->parameter->channel_number; i++)
      for (j = 0; j < ssd->parameter->chip_channel[i]; j++)
        for (k = 0; k < ssd->parameter->die_chip; k++)
          for (l = 0; l < ssd->parameter->plane_die; l++) {
            flag = 0;
            for (m = 0; m < ssd->parameter->block_plane; m++) {
              if (flag >= threshould) {
                break;
              }
              for (n = 0; n < ssd->parameter->page_block; n++) {
                rand_value = rand() % 10;
                // printf("%d\n",rand_value);
                if (rand_value < vaild_page_ratio) {
                  lpn = get_lpn_for_make_aged(ssd, large_lpn);
                  valid_state = ~(0xffffffff << (ssd->parameter->subpage_page));
                  ssd->dram->map->map_entry[lpn].pn = find_ppn(ssd, i, j, k, l, m, n);
                  ssd->dram->map->map_entry[lpn].state = ~(0xffffffff << (ssd->parameter->subpage_page));
                } else {
                  lpn = 0;
                  valid_state = 0;
                  ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head[m].invalid_page_num++;
                }
                flag++;
                ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head[m].page_head[n].valid_state = valid_state;
                ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head[m].page_head[n].free_state = 0;
                ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head[m].page_head[n].lpn = lpn;  //把valid_state free_state
                                                                                                                 // lpn都置为0表示页失效，检测的时候三项都检测，单独lpn=0可以是有效页
                ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head[m].free_page_num--;
                ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head[m].last_write_page++;
                ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].free_page--;
                ppn = find_ppn(ssd, i, j, k, l, m, n);
              }
              int tmp = 0;  // gdb断点测试
              // printf("%d\n",ssd->channel_head[i].chip_head[j].die_head[k].plane_head[l].blk_head[m].invalid_page_num);
            }
          }
  } else {
    return ssd;
  }

  return ssd;
}

/*********************************************************************************************
 *no_buffer_distribute()函数是处理当ssd没有dram的时候，
 *这是读写请求就不必再需要在buffer里面寻找，直接利用creat_sub_request()函数创建子请求，再处理。
 *********************************************************************************************/
struct ssd_info *no_buffer_distribute(struct ssd_info *ssd) {
  unsigned int lsn, lpn, last_lpn, first_lpn, complete_flag = 0, state;
  unsigned int flag = 0, flag1 = 1,
               active_region_flag = 0;  // to indicate the lsn is hitted or not
  struct request *req = NULL;
  struct sub_request *sub = NULL, *sub_r = NULL, *update = NULL;
  struct local *loc = NULL;
  struct channel_info *p_ch = NULL;

  unsigned int mask = 0;
  unsigned int offset1 = 0, offset2 = 0;
  unsigned int sub_size = 0;
  unsigned int sub_state = 0;

  ssd->dram->current_time = ssd->current_time;
  req = ssd->request_tail;
  lsn = req->lsn;
  lpn = req->lsn / ssd->parameter->subpage_page;
  last_lpn = (req->lsn + req->size - 1) / ssd->parameter->subpage_page;
  first_lpn = req->lsn / ssd->parameter->subpage_page;

  if (req->operation == READ) {
    while (lpn <= last_lpn) {
      sub_state = (ssd->dram->map->map_entry[lpn].state & 0x7fffffff);
      sub_size = size(sub_state);
      sub = creat_sub_request(ssd, lpn, sub_size, sub_state, req, req->operation);
      lpn++;
    }
  } else if (req->operation == WRITE) {
    while (lpn <= last_lpn) {
      mask = ~(0xffffffff << (ssd->parameter->subpage_page));
      state = mask;
      if (lpn == first_lpn) {
        offset1 = ssd->parameter->subpage_page - ((lpn + 1) * ssd->parameter->subpage_page - req->lsn);
        state = state & (0xffffffff << offset1);
      }
      if (lpn == last_lpn) {
        offset2 = ssd->parameter->subpage_page - ((lpn + 1) * ssd->parameter->subpage_page - (req->lsn + req->size));
        state = state & (~(0xffffffff << offset2));
      }
      sub_size = size(state);

      sub = creat_sub_request(ssd, lpn, sub_size, state, req, req->operation);
      lpn++;
    }
  }

  return ssd;
}
